package com.bjb.vr.auth.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bjb.vr.auth.bo.UmsUserDetail;
import com.bjb.vr.auth.exception.UsernameNotMatchException;
import com.bjb.vr.common.ao.UmsAccountAo;
import com.bjb.vr.common.constant.CaptchaConstants;
import com.bjb.vr.common.constant.LoginConstant;
import com.bjb.vr.common.constant.ThreadLocalConstant;
import com.bjb.vr.common.constant.TokenConstants;
import com.bjb.vr.common.dto.*;
import com.bjb.vr.common.mapper.UmsOptRoleMapper;
import com.bjb.vr.common.mapper.UmsRoleMapper;
import com.bjb.vr.common.result.BaseErrorCode;
import com.bjb.vr.common.result.CommonResult;
import com.bjb.vr.common.service.UmsAccountService;
import com.bjb.vr.common.service.UmsOptAccountService;
import com.bjb.vr.common.utils.JwtTokenUtil;
import com.bjb.vr.common.utils.RedisUtil;
import com.bjb.vr.common.utils.ThreadLocalUtil;
import com.bjb.vr.common.vo.UmsAccountVo;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.power.common.util.UUIDUtil;
import com.wf.captcha.ArithmeticCaptcha;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0.0
 * @author: HuJingBo
 * @time: 2022/4/11 15:00
 */
@Slf4j
@Service
public class LoginService implements UserDetailsService, UserDetailsPasswordService {

    private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UmsAccountService umsAccountService;

    @Autowired
    private UmsRoleMapper umsRoleMapper;

    @Resource
    private UmsOptAccountService optAccountService;

    @Resource
    private UmsOptRoleMapper optRoleMapper;


    /**
     * 刷新token
     *
     * @param refreshTokenId
     * @return
     */
    public CommonResult refreshToken(String refreshTokenId) {
        String oldToken = (String) redisUtil.hget(TokenConstants.LOGIN_TOKEN_KEY + refreshTokenId, TokenConstants.TOKEN_MAP_KEY);
        String username = (String) JwtTokenUtil.parseJWT(oldToken).get(TokenConstants.TOKEN_USERNAME);
        String tenantId = (String) JwtTokenUtil.parseJWT(oldToken).get(TokenConstants.TENANT_ID);
        String loginType = (String) JwtTokenUtil.parseJWT(oldToken).get(TokenConstants.LOGIN_TYPE);
        if (StringUtils.isBlank(username)) {
            return CommonResult.Fail(BaseErrorCode.ERROR_REFRESHMENT);
        }

        //生成新的token
        String newToken = JwtTokenUtil.createJWT(username, tenantId, loginType);
        redisUtil.hset(TokenConstants.LOGIN_TOKEN_KEY + refreshTokenId, TokenConstants.TOKEN_MAP_KEY, newToken);
        redisUtil.hset(TokenConstants.LOGIN_TOKEN_KEY + refreshTokenId, TokenConstants.TOKEN_USERNAME, username);
        redisUtil.expire(TokenConstants.LOGIN_TOKEN_KEY + refreshTokenId, TokenConstants.REFRESH_TIME, TimeUnit.MINUTES);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put(TokenConstants.TOKEN_MAP_KEY, newToken);
        jsonObject.put(TokenConstants.REFRESH_TOKEN_MAP_KEY, refreshTokenId);
        return CommonResult.Ok().setResult(jsonObject);
    }

    /**
     * 获取验证码
     *
     * @return
     */
    public CommonResult getCaptcha() {
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
        captcha.setLen(2);
        captcha.getArithmeticString();

        // 验证码存入redis
        String captchaKey = UUIDUtil.getUuid32();
        redisUtil.hset(CaptchaConstants.LOGIN_CAPTCHA_KEY + captchaKey, CaptchaConstants.CAPTCHA_KEY, captchaKey);
        redisUtil.hset(CaptchaConstants.LOGIN_CAPTCHA_KEY + captchaKey, CaptchaConstants.CAPTCHA_VALUE, captcha.text());
        redisUtil.expire(CaptchaConstants.LOGIN_CAPTCHA_KEY + captchaKey, CaptchaConstants.EXPIRATION, TimeUnit.MINUTES);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put(CaptchaConstants.CAPTCHA_IMAGE, captcha.toBase64());
        jsonObject.put(CaptchaConstants.CAPTCHA_KEY, captchaKey);
        return CommonResult.Ok().setResult(jsonObject);
    }

    /**
     * 自动更新密码
     *
     * @param user
     * @param newPassword
     * @return
     */
    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        log.info("开始自动更新密码...");
        UmsAccount umsAccount = new UmsAccount();
        umsAccount.setPassword(newPassword);
        LambdaQueryWrapper<UmsAccount> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UmsAccount::getUsername, user.getUsername());
        boolean update = umsAccountService.update(umsAccount, queryWrapper);
        log.info("用户[{}]修改密码结果：{}", user.getUsername(), update);
        return user;
    }

    /**
     * 根据用户名获取用户信息
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 判断登录平台类型
        String loginType = (String) ThreadLocalUtil.get(ThreadLocalConstant.LOGIN_TYPE);
        UmsUserDetail umsUserDetail = null;
        if (LoginConstant.MANAGER.equals(loginType)) {
            umsUserDetail = buildManagerUserInfo(username);
        } else {
            umsUserDetail = buildOperationUserInfo(username);
        }
        return umsUserDetail;
    }

    /**
     * 封装运营端用户信息
     *
     * @return
     */
    private UmsUserDetail buildOperationUserInfo(String username) {
        LambdaQueryWrapper<UmsOptAccount> lambdaQuery = Wrappers.lambdaQuery(UmsOptAccount.class);
        lambdaQuery.eq(StringUtils.isNotBlank(username), UmsOptAccount::getUsername, username);
        UmsOptAccount umsOptAccount = optAccountService.getOne(lambdaQuery);
        if (umsOptAccount == null) {
            throw new UsernameNotMatchException("用户名不存在");
        }

        // 放入线程变量
        UmsAccountVo umsAccountVo = new UmsAccountVo();
        BeanUtils.copyProperties(umsOptAccount, umsAccountVo);
        ThreadLocalUtil.set(ThreadLocalConstant.USER_ACCOUNT_INFO, umsAccountVo);

        // 封装用户信息
        UmsUserDetail umsUser = new UmsUserDetail(username, umsOptAccount.getPassword(), umsOptAccount.getStatus() == 1 ? true : false);
        umsUser.setUserId(umsOptAccount.getUserId());
        umsUser.setTenantId(umsOptAccount.getTenantId());
        umsUser.setLoginType(LoginConstant.OPERATION);

        // 查询是否需要更新密码
        umsUser.setRequireChangePass(umsOptAccount.getInitialPass() == 1 ? true : false);

        // 获取角色信息
        List<String> roles = getOptRoles(umsOptAccount.getUserId());
        umsUser.setRoles(roles);

        // 获取权限信息
        Collection<GrantedAuthority> authorities = getOptAuthorities(umsOptAccount.getUserId());
        umsUser.setAuthorities(authorities);
        log.info("运营端-用户[{}]登录信息：{}", username, umsUser);

        // 关闭放行
        ThreadLocalUtil.set(ThreadLocalConstant.NO_TENANT_ID_VERIFY, false);
        return umsUser;
    }

    /**
     * 封装教师端用户信息
     *
     * @return
     */
    private UmsUserDetail buildManagerUserInfo(String username) {
        LambdaQueryWrapper<UmsAccount> lambdaQuery = Wrappers.lambdaQuery(UmsAccount.class);
        lambdaQuery.eq(StringUtils.isNotBlank(username), UmsAccount::getUsername, username);
        UmsAccount umsAccount = umsAccountService.getOne(lambdaQuery);
        if (umsAccount == null) {
            throw new UsernameNotMatchException("用户名不存在");
        }

        // 放入线程变量
        UmsAccountVo umsAccountVo = new UmsAccountVo();
        BeanUtils.copyProperties(umsAccount, umsAccountVo);
        ThreadLocalUtil.set(ThreadLocalConstant.USER_ACCOUNT_INFO, umsAccountVo);

        // 封装用户信息
        UmsUserDetail umsUser = new UmsUserDetail(username, umsAccount.getPassword(), umsAccount.getStatus() == 1 ? true : false);
        umsUser.setUserId(umsAccount.getUserId());
        umsUser.setTenantId(umsAccount.getTenantId());
        umsUser.setLoginType(LoginConstant.MANAGER);

        // 查询是否需要更新密码
        umsUser.setRequireChangePass(umsAccount.getInitialPass() == 1 ? true : false);

        // 获取角色信息
        List<String> roles = getRoles(umsAccount.getUserId());
        umsUser.setRoles(roles);

        // 获取权限信息
        Collection<GrantedAuthority> authorities = getAuthorities(umsAccount.getUserId());
        umsUser.setAuthorities(authorities);
        log.info("教师端-用户[{}]登录信息：{}", username, umsUser);

        // 关闭放行
        ThreadLocalUtil.set(ThreadLocalConstant.NO_TENANT_ID_VERIFY, false);
        return umsUser;
    }

    /**
     * 获取用户角色信息
     *
     * @return
     */
    private List<String> getRoles(Long userId) {
        // 构造查询条件
        MPJLambdaWrapper<UmsRole> joinQueryWrapper = new MPJLambdaWrapper<>();
        joinQueryWrapper
                .select(UmsRole::getRoleName)
                .innerJoin(UmsUserRole.class, UmsUserRole::getRoleId, UmsRole::getRoleId);
        joinQueryWrapper.eq(UmsUserRole::getUserId, userId);
        // 执行查询
        return umsRoleMapper.selectJoinList(String.class, joinQueryWrapper);
    }

    /**
     * 获取用户权限信息
     *
     * @param userId
     * @return
     */
    private Collection<GrantedAuthority> getAuthorities(Long userId) {
        List<String> permissions = umsRoleMapper.queryPermsByUserId(userId);
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        permissions.stream().forEach(perms -> {
            authList.add(new SimpleGrantedAuthority(perms));
        });
        return authList;
    }

    /**
     * 获取用户角色信息--运营端
     *
     * @return
     */
    private List<String> getOptRoles(Long userId) {
        // 构造查询条件
        MPJLambdaWrapper<UmsOptRole> joinQueryWrapper = new MPJLambdaWrapper<>();
        joinQueryWrapper
                .select(UmsOptRole::getRoleName)
                .innerJoin(UmsOptUserRole.class, UmsOptUserRole::getRoleId, UmsOptRole::getRoleId);
        joinQueryWrapper.eq(UmsOptUserRole::getUserId, userId);
        // 执行查询
        return optRoleMapper.selectJoinList(String.class, joinQueryWrapper);
    }

    /**
     * 获取用户权限信息--运营端
     *
     * @param userId
     * @return
     */
    private Collection<GrantedAuthority> getOptAuthorities(Long userId) {
        List<String> permissions = optRoleMapper.queryPermsByUserId(userId);
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        permissions.stream().forEach(perms -> {
            authList.add(new SimpleGrantedAuthority(perms));
        });
        return authList;
    }

    /**
     * 获取登录用户信息
     *
     * @param authorization
     * @return
     */
    public UmsAccountVo getUserAccount(String authorization) {
        UmsAccountAo umsAccountAo = UmsAccountAo.builder().authorization(authorization).build();
        Claims claims = JwtTokenUtil.parseJWT(umsAccountAo.getAuthorization());
        String loginType = String.valueOf(claims.get(TokenConstants.LOGIN_TYPE));
        UmsAccountVo umsAccountVo = null;
        if (LoginConstant.MANAGER.equals(loginType)) {
            umsAccountVo = umsAccountService.getAccountInfo(umsAccountAo);
        } else {
            umsAccountVo = optAccountService.getAccountInfo(umsAccountAo);
        }
        return umsAccountVo;
    }

}
